<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		
		<script type="text/javascript">
			
			
			
		</script>
	
		<!-- 
		一 组件
		1. 组件 （Component）是 Vue.js 最强大的功能之一，组件是一个自定义元素或称为一个模块，
		 包括所需的模板（HTML）、逻辑（JavaScript）和样式（CSS）。
		 **组件化开发的特点：**
		 * 标准
		 * 分治
		 * 重用
		 * 组合
		 组件也是有 全局（component） 与 局部（components） 之分。
		
		2. 组件名称命名方式
		 * 短横线方式（推荐）
		   * my-component
		 * 大驼峰方式（只能在其他组件模板字符串中使用，不能在HTML模板中**直接**使用）
		   * MyComponent
		大驼峰式组件名不能在HTML模板中直接使用，如果需要在HTML模板中使用，需要将其进行特定规则转化：
		* 首字母从大写转为小写
		* 后续每遇到大写字母都要转化成小写并且在转化后的小写字母前加 -
		例如， WoDeZuJian 这个大驼峰组件名在HTML中使用的时候需要写成 wo-de-zu-jian	
		
		2.1局部组件使用
		①创建一个 vue   按照 上方所述命名方式  
		②在需要使用这个组件的vue里面 引入 这个 组件 
		import HelloWorld from './components/HelloWorld.vue'
		③注册这个组件
			export default {
				name: 'App',
				components:{
					HelloWorld
				}
			}
		④使用这个组件
		<HelloWorld></HelloWorld>
		或者<hello-world></hello-world>

		2.2全局组件注册形式如下：
		// 声明全局组件 
		在main.js里面
		import MyChildren from '@/components/MyChildren'
		Vue.component('MyChildren',MyChildren)
		
		3、VueComponent
		组件本质是一个名为VueComponent的构造函数，且不是程序员定义的，是Vue.extend生成的。
		我们只需要写<school/>或<school></school>. Vue解析时会帮我们创建组件的实例对象，即Vue帮我们执行: new VueComponent(options)。
		每次调用Vue.extend,返回的都是一个全新的VueComponent!!!
		关于this指向:
		(1).组件配置中:
		data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是[ VueComponent实例对象:VC]。
		(2).new Vue(options)配置中:
		data函数、methods中的两 数、watch中的两数、computed中 的函数它们的this均是[Vue实例对象:VM]。
		vm管理着vc（vm.$children)，组件是可复用的vue实例，所以它们与new Vue接收相同的选项，但它们也有区别，不能同等看待。
		
		4、Vue和VueComponent的关系
		VueComponent.prototype._ proto_ === Vue.prototype 让组件实例对象(vc) 可以访问到Vue原型上的属性、方法。


		
		二.组件间传值
		1.父->子传值  ---单向数据流--- 数据 由  A组件  传递至  B组件   当A组件内修改数据 B组件也会同步更新  但是不允许 B修改传过去的数据
		 父组件以属性的形式绑定值到子组件身上
		 子组件通过使用配置props接收
		 
		 
		 ①父组件调用组件的适合 绑定属性 传值
		 <MyChild content='宝马'></MyChild>
		 如果传过去的是变量
		 data(){
			 return {
				 car:'奔驰'
			 }
		 }
		 <MyChild :content='car'></MyChild>
		 ②子组件通过props接受
		 * props是单向绑定的（只读属性）：当父组件的属性变化时，将传导给子组件，但是反过来不会
		 * props属性支持两种常见的写法形式
		  props:["propa"],
			props:{
					'title':String,
					'likes': Number,
					'ispublished': Boolean,
					'commentids': Array,
					'author': Object,
				},*/
			props: {
				propb: [String, Number],// 传的参数可以是多种数据类型
				propc: {
					type: String,
					required: true //必填项
				},
				propd: {
					type: Number,
					default: 100//当没传参数时，不会报错，而会显示默认值
				},
				prope: {
					type: Object,
					// 对象或数组默认值必须从一个工厂函数获取
					default: function () {
						return { message: 'hello' }
					}
				},
				// 自定义验证函数
				propf: {
					validator: function (value) {
						// 这个值必须匹配下列字符串中的一个
						return ['success', 'warning', 'danger'].indexOf(value) !== -1
					}
				}
			},
			
			单项数据流
			
		 2.子->父传值
		 子组件模版内容中用 $emit() 定义 自定义事件 ， $emit() 方法有2个参数
		 * * 第一个参数为自定义的事件名称
		   * 第二个参数为需要传递的数据（可选）
		 * 父组件模板内容中的子组件占位标签上用v-on（或@）绑定子组件定义的自定义事件名，监听子组件的事件，实现通信
		 * 
		 子组件  
		 this.$emit('changeCar','凯迪拉克')
		 父组件绑定事件
		 	<MyChild :content='car' @changeCar='cCar'></MyChild>
			监听事件的函数
			methods:{
				cCar(car){
					//接受到子组件传递过来的 car--'凯迪拉克'
					console.log('父组件',car);
					//改变当前的属性
					this.car = car
				}
			}
		 * 
		 
		 3.ref
		 ref 属性被用来给元素或子组件注册引用信息，引用信息将会注册在父组件的 $refs 对象上。
		 
		 如果在普通的 DOM 元素上使用 ref 属性，则引用指向的就是 DOM 元素；
		 
		 如果 ref 属性用在子组件上，引用就指向子组件**实例**。
		 
		 * ref 放在标签上，拿到的是原生节点
		 * ref 放在组件上 ,  拿到的是组件实例
		 
		 * 原理：在父组件中通过 ref 属性（会被注册到父组件的 $refs 对象上）拿到组件/DOM对象，
		 * 从而得到组件/DOM中的**所有的信息**，也包括值
		 * 注意：
				ref 属性这种获取子元素/组件的方式虽然写法简单，容易上手，但是其由于权限过于开放，
				不推荐使用，有安全问题。（不仅可以获取值，还可以获取其他所有的元素/组件的数据
				，甚至可以修改这些数据。） 性能不好
			(谁的属性  谁来改)
			this.$children
			获取子组件的实例对象 
			this.$parent
			获取父组件的实例对象
			this.$root
			获取根组件的实例对象 也就是  mainjs里面 new Vue 生成的实例对象
			
			
			4. 隔代组件通信 provide + inject 
			我们可以通过 provide来提供数据：
			可以通过 provide 方法来定义每个 Property；
			在 后代组件 中可以通过 inject 来注入需要的属性和对应的值：
			可以通过 inject 来注入需要的内容；
			
			爷组件  
			provide: {
				kangxiToYongzheng: '康熙皇帝对雍正皇帝说："你是我儿子"',
			},
			孙组件  
			inject: ['kangxiToYongzheng'] 使用方法等同于props

			如果需要想隔代组件 传输 自己实例里面的属性
			错误方法  因为在对象里面 访问不到 this 所以会报错
			provide: {
				color: this.color
			},
			正确方法  使用函数  return一个对象 就可以访问 this实例
			provide() {
			  return {
			    color: this.color,
			  };
			}
			接收方法
			① 一个字符串数组。
			inject: ['content']
			② 一个对象，对象的key是本地的绑定名，value是provide定义的名字。
			inject: {
			  content: 'content'
			}
			
			③ 一个对象，区分上一种方式是为了设置默认值。对象的key是本地的绑定名，value也是一个对象。该对象有两个键，一个是from（provide定义的名字），另一个是default（默认值）。
			inject: {
			  content: {
			    from:'content',
			    default:'hello world'
			  }
			}
			
			
			提示：provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而，如果你传入了一个可监
			听的对象，那么其对象的 property 还是可响应的。 也就是指，我们需要人为的将这组数据关系变
			成可响应的，哦，我们之前的foo是一个字符串，基本数据类型是不具有响应特性的，那么，我们可能
			需要传递一个对象。
			provide() {
			    return {
			      foo: this.foo
			    };
			  },
			data() {
				return {
					foo: {
						aaa: "测试"
					},,
				};
			}，
			
			5.$attrs  $listeners
			中间组件 v-bind='$attrs' v-on='$listeners'
			
			6.EventBus --任意组件的通信
			建立事件中心
			在mainjs 里面 
			Vue.prototype.$bus = new Vue()
			传递数据
			this.$bus.$emit('自定义事件名',数据)
			接收数据
			this.$bus.$on('自定义事件名',data=>{
				console.log(data);
			})
			销毁事件中心
			this.$bus.$off('自定义事件名')

			7.订阅者发布模式--设计模式
			cnpm i pubsub-js
			import pubsub from 'pubsub-js';
			接受  
			created(){
				this.pubId = pubsub.subscribe('hello',function(dataName,message){
						console.log('school:'+'有人发布了hello消息,hello消息的回调执行了');
						console.log('消息名是:',dataName,"消息内容是：",message);
					})
				},
			发送数据 
			pubsub.publish('hello',666)
			解绑订阅 
			pubsub.unsubscribe(this.pubId)
		 -->
	</body>
</html>